#include "Utils.h"

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/Importer.hpp>
#include <unordered_map>
//#include <SDL.h>
#include <glad/glad.h>
#include <cstdlib>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

//sourceCode: https://github.com/hasinaxp/skeletal_animation-_assimp_opengl
//这一次尝试导入FBX骨骼动画...
/*
opengl skeletal animation demo
*/
bool firstMouse = true;
float yaw   = -90.0f;	// yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right so we initially rotate a bit to the left.
float pitch =  0.0f;
float lastX =  800.0f / 2.0;
float lastY =  600.0 / 2.0;
float fov   =  45.0f;
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  9.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);
float deltaTime = 0.0f;	// time between current frame and last frame
float lastFrame = 0.0f;

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);


// settings
const unsigned int windowWidth = 1080;
const unsigned int windowHeight = 1080;
const char* vertexShaderSource = R"(
	#version 440 core
	layout (location = 0) in vec3 position;
	layout (location = 1) in vec3 normal;
	layout (location = 2) in vec2 uv;
	layout (location = 3) in vec4 boneIds;
	layout (location = 4) in vec4 boneWeights;

	out vec2 tex_cord;
	out vec3 v_normal;
	out vec3 v_pos;
	out vec4 bw;

	uniform mat4 bone_transforms[80];
	uniform mat4 view_projection_matrix;
	uniform mat4 model_matrix;

	void main()
	{
		bw = vec4(0);
		if(int(boneIds.x) == 1)
		bw.z = boneIds.x;
		//boneWeights = normalize(boneWeights);
		mat4 boneTransform  =  mat4(0.0);
		boneTransform  +=    bone_transforms[int(boneIds.x)] * boneWeights.x;
		boneTransform  +=    bone_transforms[int(boneIds.y)] * boneWeights.y;
		boneTransform  +=    bone_transforms[int(boneIds.z)] * boneWeights.z;
		boneTransform  +=    bone_transforms[int(boneIds.w)] * boneWeights.w;
		vec4 pos =boneTransform * vec4(position, 1.0);
		gl_Position = view_projection_matrix * model_matrix * pos;
		v_pos = vec3(model_matrix * boneTransform * pos);
		tex_cord = uv;
		v_normal = mat3(transpose(inverse(model_matrix * boneTransform))) * normal;
		v_normal = normalize(v_normal);
	}

	)";
const char* fragmentShaderSource = R"(
	#version 440 core

	in vec2 tex_cord;
	in vec3 v_normal;
	in vec3 v_pos;
	in vec4 bw;
	out vec4 color;

	uniform sampler2D diff_texture;

	vec3 lightPos = vec3(0.2, 1.0, -3.0);

	void main()
	{
		vec3 lightDir = normalize(lightPos - v_pos);
		float diff = max(dot(v_normal, lightDir), 0.2);
		vec3 dCol = diff * texture(diff_texture, tex_cord).rgb;
		color = vec4(dCol, 1);
	}
	)";


// vertex of an animated model
struct Vertex {
    glm::vec3 position;							//顶点
    glm::vec3 normal;							//法向
    glm::vec2 uv;								//纹理坐标
    glm::vec4 boneIds = glm::vec4(0);			//骨骼
    glm::vec4 boneWeights = glm::vec4(0.0f);	//骨骼的权重
};

// structure to hold bone tree (skeleton) 骨骼树
struct Bone {
    int id = 0; // position of the bone in final upload array
    std::string name = "";										//名称
    glm::mat4 offset = glm::mat4(1.0f);
    std::vector<Bone> children = {};							//子结点集合
};

// sturction representing an animation track 动画描述
struct BoneTransformTrack {
    std::vector<float> positionTimestamps = {};					//移动所费的时间
    std::vector<float> rotationTimestamps = {};
    std::vector<float> scaleTimestamps = {};

    std::vector<glm::vec3> positions = {};						//移动
    std::vector<glm::quat> rotations = {};
    std::vector<glm::vec3> scales = {};
};

// structure containing animation information 动画
struct Animation {
    float duration = 0.0f;														//动画时间
    float ticksPerSecond = 1.0f;												//时间单位
    std::unordered_map<std::string, BoneTransformTrack> boneTransforms = {};	//动画序列map
};



// a recursive function to read all bones and form skeleton
bool readSkeleton(Bone& boneOutput, aiNode* node, std::unordered_map<std::string, std::pair<int, glm::mat4>>& boneInfoTable) {

    if (boneInfoTable.find(node->mName.C_Str()) != boneInfoTable.end()) { // if node is actually a bone
        boneOutput.name = node->mName.C_Str();
        boneOutput.id = boneInfoTable[boneOutput.name].first;
        boneOutput.offset = boneInfoTable[boneOutput.name].second;

        //打印骨骼信息
        std::cout << "readSkeleton() bone=" << boneOutput.name << std::endl;

        for (int i = 0; i < node->mNumChildren; i++) {
            Bone child;
            readSkeleton(child, node->mChildren[i], boneInfoTable);
            boneOutput.children.push_back(child);
        }
        return true;
    }
    else { // find bones in children
        for (int i = 0; i < node->mNumChildren; i++) {
            if (readSkeleton(boneOutput, node->mChildren[i], boneInfoTable)) {
                return true;
            }

        }
    }
    return false;
}

void loadModel(const aiScene* scene, aiMesh* mesh, std::vector<Vertex>& verticesOutput, std::vector<uint>& indicesOutput, Bone& skeletonOutput, uint& nBoneCount) {

    verticesOutput = {};
    indicesOutput = {};
    //load position, normal, uv
    for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
        //process position
        Vertex vertex;
        glm::vec3 vector;
        vector.x = mesh->mVertices[i].x;
        vector.y = mesh->mVertices[i].y;
        vector.z = mesh->mVertices[i].z;
        vertex.position = vector;
        //process normal
        vector.x = mesh->mNormals[i].x;
        vector.y = mesh->mNormals[i].y;
        vector.z = mesh->mNormals[i].z;
        vertex.normal = vector;
        //process uv
        glm::vec2 vec;
        vec.x = mesh->mTextureCoords[0][i].x;
        vec.y = mesh->mTextureCoords[0][i].y;
        vertex.uv = vec;

        vertex.boneIds = glm::ivec4(0);
        vertex.boneWeights = glm::vec4(0.0f);

        verticesOutput.push_back(vertex);
    }

    //load boneData to vertices
    std::unordered_map<std::string, std::pair<int, glm::mat4>> boneInfo = {};
    std::vector<uint> boneCounts;
    boneCounts.resize(verticesOutput.size(), 0);
    nBoneCount = mesh->mNumBones;
    //打印骨骼总数
    std::cout << "loadModel() nBoneCount=" << nBoneCount << "\n" << std::endl;
    //loop through each bone
    for (uint i = 0; i < nBoneCount; i++) {
        aiBone* bone = mesh->mBones[i];
        glm::mat4 m = assimpToGlmMatrix(bone->mOffsetMatrix);
        boneInfo[bone->mName.C_Str()] = { i, m };

        //loop through each vertex that have that bone
        for (int j = 0; j < bone->mNumWeights; j++) {
            uint id = bone->mWeights[j].mVertexId;
            float weight = bone->mWeights[j].mWeight;
            std::cout<<id<<std::endl;
            boneCounts[id]++;
            switch (boneCounts[id]) {
                case 1:
                    verticesOutput[id].boneIds.x = i;
                    verticesOutput[id].boneWeights.x = weight;
                    break;
                case 2:
                    verticesOutput[id].boneIds.y = i;
                    verticesOutput[id].boneWeights.y = weight;
                    break;
                case 3:
                    verticesOutput[id].boneIds.z = i;
                    verticesOutput[id].boneWeights.z = weight;
                    break;
                case 4:
                    verticesOutput[id].boneIds.w = i;
                    verticesOutput[id].boneWeights.w = weight;
                    break;
                default:
                    //std::cout << "err: unable to allocate bone to vertex" << std::endl;
                    break;

            }
        }
    }

    //normalize weights to make all weights sum 1
    for (int i = 0; i < verticesOutput.size(); i++) {
        glm::vec4& boneWeights = verticesOutput[i].boneWeights;
        float totalWeight = boneWeights.x + boneWeights.y + boneWeights.z + boneWeights.w;
        if (totalWeight > 0.0f) {
            verticesOutput[i].boneWeights = glm::vec4(
                    boneWeights.x / totalWeight,
                    boneWeights.y / totalWeight,
                    boneWeights.z / totalWeight,
                    boneWeights.w / totalWeight
            );
        }
    }


    //load indices
    for (int i = 0; i < mesh->mNumFaces; i++) {
        aiFace& face = mesh->mFaces[i];
        for (unsigned int j = 0; j < face.mNumIndices; j++)
            indicesOutput.push_back(face.mIndices[j]);
    }

    // create bone hirerchy
    readSkeleton(skeletonOutput, scene->mRootNode, boneInfo);
}

void loadAnimation(const aiScene* scene, Animation& animation) {
    //loading  first Animation
    aiAnimation* anim = scene->mAnimations[0];

    if (anim->mTicksPerSecond != 0.0f)
        animation.ticksPerSecond = anim->mTicksPerSecond;
    else
        animation.ticksPerSecond = 1;


    animation.duration = anim->mDuration * anim->mTicksPerSecond;
    animation.boneTransforms = {};

    //duration 动画时间间隔  ticksPerSecond 时间单位
    std::cout << "loadAnimation() ticksPerSecond=" << animation.ticksPerSecond << " duration=" << animation.duration << "\n" << std::endl;

    //现在来修复这个bug
    bool checkAssimpFbx = false;						//是否是AssimpFbx动画
    std::string assimpFbxStr;							//存储AssimpFbx动画的名称
    std::vector<BoneTransformTrack> assimpFbxVector;	//存储AssimpFbx的动画序列
    //load positions rotations and scales for each bone
    // each channel represents each bone
    for (int i = 0; i < anim->mNumChannels; i++) {
        aiNodeAnim* channel = anim->mChannels[i];
        BoneTransformTrack track;
        for (int j = 0; j < channel->mNumPositionKeys; j++) {
            track.positionTimestamps.push_back(channel->mPositionKeys[j].mTime);
            track.positions.push_back(assimpToGlmVec3(channel->mPositionKeys[j].mValue));
        }
        for (int j = 0; j < channel->mNumRotationKeys; j++) {
            track.rotationTimestamps.push_back(channel->mRotationKeys[j].mTime);
            track.rotations.push_back(assimpToGlmQuat(channel->mRotationKeys[j].mValue));

        }
        for (int j = 0; j < channel->mNumScalingKeys; j++) {
            track.scaleTimestamps.push_back(channel->mScalingKeys[j].mTime);
            track.scales.push_back(assimpToGlmVec3(channel->mScalingKeys[j].mValue));

        }

        std::string nName(channel->mNodeName.C_Str());
        std::string::size_type ret = nName.find("_$AssimpFbx$_");
        //如果是FBX动画则先暂存
        if (ret != std::string::npos)
        {
            checkAssimpFbx = true;
            assimpFbxStr = nName.substr(0, ret);
            assimpFbxVector.push_back(track);
            std::cout << "loadAnimation() print assimpFbxStr=" << assimpFbxStr << " OldStr=" << nName << std::endl;
        }
        else {
            //如果是FBX动画刚结束，则暂存动画保存至animation
            if (checkAssimpFbx)
            {
                checkAssimpFbx = false;
                //用于暂存的变量
                BoneTransformTrack outTrack;
                for (int i = 0; i < assimpFbxVector.size(); i++)
                {
                    BoneTransformTrack item = assimpFbxVector[i];
                    if (item.positions.size() > 1)
                    {
                        outTrack.positionTimestamps = item.positionTimestamps;
                        outTrack.positions = item.positions;
                    }
                    if (item.rotations.size() > 1)
                    {
                        outTrack.rotationTimestamps = item.rotationTimestamps;
                        outTrack.rotations = item.rotations;
                    }
                    if (item.scales.size() > 1)
                    {
                        outTrack.scaleTimestamps = item.scaleTimestamps;
                        outTrack.scales = item.scales;
                    }
                }
                std::cout << "loadAnimation() animation FBX=" << assimpFbxStr << std::endl;
                animation.boneTransforms[assimpFbxStr] = outTrack;

            }
            //打印动画信息 骨骼(bone)与动画(animation)信息是一对一关系!
            std::cout << "loadAnimation() animation=" << channel->mNodeName.C_Str() << std::endl;
            animation.boneTransforms[channel->mNodeName.C_Str()] = track;
        }

    }
}

unsigned int createVertexArray(std::vector<Vertex>& vertices, std::vector<unsigned int> indices) {
    unsigned int
            vao ,
            vbo ,
            ebo ;

    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);
    glGenBuffers(1, &ebo);

    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, position));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, normal));
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, uv));
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, boneIds));
    glEnableVertexAttribArray(4);
    glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, boneWeights));

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(uint), &indices[0], GL_STATIC_DRAW);
    glBindVertexArray(0);
    return vao;
}

uint createTexture(std::string filepath) {
    uint textureId = 0;
    int width, height, nrChannels;
    byte* data = stbi_load(filepath.c_str(), &width, &height, &nrChannels, 4);
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

    stbi_image_free(data);
    glBindTexture(GL_TEXTURE_2D, 0);
    return textureId;
}



std::pair<uint, float> getTimeFraction(std::vector<float>& times, float& dt) {
    uint segment = 0;//times是个空数组 故报错
    //dt = 276.320007 但times[1]=276 segment百分百的越界
    while (dt > times[segment])
    {
        segment++;
        if (segment >= times.size()) {
            segment--;
            break;
        }
    }

    float start = times[segment - 1];
    float end = times[segment];
    float frac = (dt - start) / (end - start);
    return { segment, frac };
}



void getPose(Animation& animation, Bone& skeletion, float dt, std::vector<glm::mat4>& output, glm::mat4& parentTransform, glm::mat4& globalInverseTransform) {
    BoneTransformTrack& btt = animation.boneTransforms[skeletion.name];//根据骨骼名"mixamorig:Hips" btt没有找到动画信息! 这里是出事地点，却不是案发现场
    //因为骨骼(bone)与动画(animation)信息是一对一关系
    //所以问题就很明显:
    //在控制台里 骨骼mixamorig:Hips 对应的动画信息是 mixamorig:Hips_$AssimpFbx$_Translation Hips_$AssimpFbx$_Rotation Hips_$AssimpFbx$_Scaling三个动画
    //所以需要将刚才三个动画合成一个动画，并且名称改成 mixamorig:Hips

    //再一次的vector溢出表示仍然存在着更深的bug
    //如果老实的一句一句的跟断点将永远找不着出事地点
    //从控制台里可以看到bone=空

    //但愿这是最后一次bug。 非常确定这个bug藏得更深。
    //从控制台可以看到 打印bone到 mixamorig:LeftHand 卡住。但却有无数个循环。。。接着看吧
    //图像编程里最常见的BUG就是循环里或者嵌套循环里有BUG。。。
    //利用时间刻度来打印看看吧。
    //利用时间刻度和骨骼名两重断点的判断就能找到出事地点
    if (btt.positions.size() == 0 || btt.rotations.size() == 0 || btt.scales.size() == 0)
        return;

    //if(skeletion.name == "mixamorig:LeftHand" )
    //std::cout << "getPose() bone=" << skeletion.name << std::endl;

    dt = fmod(dt, animation.duration);
    std::pair<uint, float> fp;
    //calculate interpolated position
    fp = getTimeFraction(btt.positionTimestamps, dt);

    glm::vec3 position1 = btt.positions[fp.first - 1];
    glm::vec3 position2 = btt.positions[fp.first];

    glm::vec3 position = glm::mix(position1, position2, fp.second);

    //calculate interpolated rotation
    fp = getTimeFraction(btt.rotationTimestamps, dt);
    glm::quat rotation1 = btt.rotations[fp.first - 1];
    glm::quat rotation2 = btt.rotations[fp.first];

    glm::quat rotation = glm::slerp(rotation1, rotation2, fp.second);

    //calculate interpolated scale
    fp = getTimeFraction(btt.scaleTimestamps, dt);
    glm::vec3 scale1 = btt.scales[fp.first - 1];
    glm::vec3 scale2 = btt.scales[fp.first];

    glm::vec3 scale = glm::mix(scale1, scale2, fp.second);

    glm::mat4 positionMat = glm::mat4(1.0),
            scaleMat = glm::mat4(1.0);


    // calculate localTransform
    positionMat = glm::translate(positionMat, position);
    glm::mat4 rotationMat = glm::toMat4(rotation);
    scaleMat = glm::scale(scaleMat, scale);
    glm::mat4 localTransform = positionMat * rotationMat * scaleMat;
    glm::mat4 globalTransform = parentTransform * localTransform;

    output[skeletion.id] = globalInverseTransform * globalTransform * skeletion.offset;
    //update values for children bones
    for (Bone& child : skeletion.children) {
        getPose(animation, child, dt, output, globalTransform, globalInverseTransform);
    }
    //std::cout << dt << " => " << position.x << ":" << position.y << ":" << position.z << ":" << std::endl;
}


int main(int argc, char** argv) {

    //init
//    int windowWidth, windowHeight;
//    SDL_Window* window = initWindow(windowWidth, windowHeight);
    bool isRunning = true;

    //load model file
    Assimp::Importer importer;
    const char* filePath = "../man/paimon.fbx";
//    const char* filePath = "../man/boss_lan.FBX";
    const aiScene* scene = importer.ReadFile(filePath, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals);

    if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
        std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
    }
    aiMesh* mesh = scene->mMeshes[0];

    std::vector<Vertex> vertices = {};
    std::vector<uint> indices = {};
    uint boneCount = 0;
    Animation animation;
    uint vao = 0;
    Bone skeleton;
    uint diffuseTexture;

    //as the name suggests just inverse the global transform
    glm::mat4 globalInverseTransform = assimpToGlmMatrix(scene->mRootNode->mTransformation);
    globalInverseTransform = glm::inverse(globalInverseTransform);


    loadModel(scene, mesh, vertices, indices, skeleton, boneCount);
    loadAnimation(scene, animation);


    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetCursorPosCallback(window, mouse_callback);



    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }





    vao = createVertexArray(vertices, indices);
    diffuseTexture = createTexture("../man/Atlas_30746.png");
//    diffuseTexture = createTexture("../man/boss_lan.jpg");

    glm::mat4 identity(1.0);

    //currentPose is held in this vector and uploaded to gpu as a matrix array uniform
    std::vector<glm::mat4> currentPose = {};
    currentPose.resize(boneCount, identity); // use this for no animation

    uint shader = createShader(vertexShaderSource, fragmentShaderSource);

    //get all shader uniform locations
    uint viewProjectionMatrixLocation = glGetUniformLocation(shader, "view_projection_matrix");
    uint modelMatrixLocation = glGetUniformLocation(shader, "model_matrix");
    uint boneMatricesLocation = glGetUniformLocation(shader, "bone_transforms");
    uint textureLocation = glGetUniformLocation(shader, "diff_texture");



    // initialize projection view and model matrix
    glm::mat4 projectionMatrix = glm::perspective(1.0f, (float)windowWidth / windowHeight, 0.01f, 1000.0f);
    //摄像机靠后一点
    glm::mat4 viewMatrix =glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
    glm::mat4 viewProjectionMatrix = projectionMatrix * viewMatrix;

    glm::mat4 modelMatrix(1.0f);
    modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0f, 0.0f, 0.0f));
    //modelMatrix = glm::scale(modelMatrix, glm::vec3(.2f, .2f, .2f));

    //变大模型
    modelMatrix = glm::scale(modelMatrix, glm::vec3(1.90f, 1.9f, 1.9f));
    //向上轴混乱很常见。比如opengl的向上轴是y轴，但3dmax的向上轴是z轴。。。
    modelMatrix = glm::rotate(modelMatrix, 90.0f, glm::vec3(0, 0, -90));
//    modelMatrix = glm::rotate(modelMatrix, 90.0f, glm::vec3(0, 0, 90));

    glEnable(GL_DEPTH_TEST);

    while (!glfwWindowShouldClose(window)) {


        processInput(window);
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        viewMatrix =glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        viewProjectionMatrix = projectionMatrix * viewMatrix;

        std::cout<< cameraPos.x<<std::endl;
        //调整动画时间
        float elapsedTime = (float)glfwGetTime()*10;
        //if (elapsedTime > 276.0f)
        //	std::cout << "stop !!!" << std::endl;

        //关闭以前的旋转模型动画
        //float dAngle = elapsedTime * 0.002;
        //modelMatrix = glm::rotate(modelMatrix, dAngle, glm::vec3(0, 1, 0));
        //std::cout << " elapsedTime=" << elapsedTime << std::endl;

        //最后一部分动画坏掉了。设置一个时间周期。到期就回到原来的起点。或者到期就让动画停下来。
        //游戏模型动画，个性人物秀，三种攻击动作，挨打动作，走路，跑步，死亡。全都有。成了
        //这个例子代码只有几百行，入门容易。但功能就惨不忍睹了。
        //对于多个骨骼动画。本例仍然不支持。很多人说没有例子。其实LeanrnOpenglCN上就说明白了。若你有基础应该不是难事。
        elapsedTime = (int)elapsedTime;
        elapsedTime = elapsedTime < 20.0f ? 20.0f : elapsedTime;
        getPose(animation, skeleton, elapsedTime, currentPose, identity, globalInverseTransform);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//        glClear(GL_COLOR_BUFFER_BIT);
        glUseProgram(shader);
        glUniformMatrix4fv(viewProjectionMatrixLocation, 1, GL_FALSE, glm::value_ptr(viewProjectionMatrix));
        glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, glm::value_ptr(modelMatrix));
        glUniformMatrix4fv(boneMatricesLocation, boneCount, GL_FALSE, glm::value_ptr(currentPose[0]));

        glBindVertexArray(vao);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, diffuseTexture);
        glUniform1i(textureLocation, 0);

        glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);
        glfwPollEvents();

//        glfwTerminate();
    }

    return 0;
}

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    float cameraSpeed = static_cast<float>(2.5 * deltaTime);
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        cameraPos += cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        cameraPos -= cameraSpeed * cameraFront;
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
    float xpos = static_cast<float>(xposIn);
    float ypos = static_cast<float>(yposIn);

    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
    lastX = xpos;
    lastY = ypos;

    float sensitivity = 0.1f; // change this value to your liking
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw += xoffset;
    pitch += yoffset;

    // make sure that when pitch is out of bounds, screen doesn't get flipped
    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);
}